//
// Created by Crestimes on 24-5-9.
//

#include "Scanner.h"

#include <iostream>
#include <utility>

Scanner::Scanner(std::string s): buffer(std::move(s)) {}

std::vector<Token> Scanner::getTokens() {
    std::vector<Token> tokens;
    for (int pos = 0; pos < buffer.length(); pos++) {
        char c = buffer[pos];
        currentToken += c;
        switch (state) {
            case START:
                // 有共用情况(如<可能也是<=的这类情况，需要多次读取讨论的就进INXXX)
                if (isdigit(c)) {
                    // 数字
                    state = IN_NUM;
                }
                else if (isalpha(c)) {
                    // 变量/关键字
                    state = IN_ID;
                }
                else if (c == '<') {
                    // <开头的处理
                    state = IN_LT;
                }
                else if (c == '>') {
                    // >开头的处理
                    state = IN_GT;
                }
                else if (c == '+') {
                    // +开头的处理
                    state = IN_ADD;
                }
                else if (c == '-') {
                    // -开头的处理
                    state = IN_SUB;
                }
                else if (c == '=') {
                    // =开头的处理
                    state = IN_EQU;
                }
                else if (c == ':') {
                    // :=处理，下一个必须是=
                    state = IN_EXP_EQ;
                }
                else if ((c == ' ') || (c == '\t') || (c == '\n')) {
                    // 跳过，去掉存的c
                    currentToken.pop_back();
                }
                else if (c == '{') {
                    // 注释
                    state = IN_IGNORE;
                }
                else if (c == '$') {
                    // 正则表达式的处理
                    state = IN_REG;
                    // 先存下来
                    tokens.emplace_back(RE_LEFT_FLAG, currentToken);
                    currentToken.pop_back();
                }
                // 否则就直接匹配一个字符
                else {
                    state = DONE;
                    currentTokenType = token2tokenType[c];
                }
                break;
            case IN_IGNORE:
                if (c == '}') {
                    currentToken = ""; // 注释不存东西
                    currentTokenType = IGNORE;
                    state = DONE;
                }
                break;
            case IN_NUM:
                // 存到不是数字为止
                if (!isdigit(c)) {
                    pos--; // 回到上一个
                    currentToken.pop_back(); // 去掉这个c
                    currentTokenType = NUMBER;
                    state = DONE;
                }
                break;
            case IN_ID:
                // 存到不是字母或数字为止
                if (!isalpha(c) && !isdigit(c) && c != '_') {
                    pos--; // 回到上一个
                    currentToken.pop_back(); // 去掉这个c
                    state = DONE;
                    // 还要看是否是关键字
                    if (keyWord2tokenType.count(currentToken)) {
                        currentTokenType = keyWord2tokenType[currentToken];
                    }
                    else currentTokenType = IDENTIFIER;
                }
                break;
            case IN_LT:
                if (c == '=' || c == '>') {
                    // <= 或 <>
                    state = DONE;
                }
                else {
                    // <
                    pos--; // 回到上一个
                    currentToken.pop_back(); // 去掉这个c
                    state = DONE;
                }
                currentTokenType = OP_CAMP;
                break;
            case IN_GT:
                if (c == '=') {
                    // >=
                    state = DONE;
                }
                else {
                    // >
                    pos--; // 回到上一个
                    currentToken.pop_back(); // 去掉这个c
                    state = DONE;
                }
                currentTokenType = OP_CAMP;
                break;
            case IN_ADD:
                if (c == '+') {
                    // ++
                    state = DONE;
                    currentTokenType = OP_SELF;
                }
                else {
                    // +
                    pos--; // 回到上一个
                    currentToken.pop_back(); // 去掉这个c
                    state = DONE;
                    currentTokenType = OP_LEVEL0;
                }
                break;
            case IN_SUB:
                if (c == '-') {
                    // --
                    state = DONE;
                    currentTokenType = OP_SELF;
                }
                else {
                    // -
                    pos--; // 回到上一个
                    currentToken.pop_back(); // 去掉这个c
                    state = DONE;
                    currentTokenType = OP_LEVEL0;
                }
                break;
            case IN_EQU:
                if (c == '=') {
                    // ==
                    state = DONE;
                    currentTokenType = REG_EQ;
                }
                else {
                    // =
                    pos--; // 回到上一个
                    currentToken.pop_back(); // 去掉这个c
                    state = DONE;
                    currentTokenType = OP_CAMP;
                }
                break;
            case IN_EXP_EQ:
                if (c == '=') {
                    // :=
                    state = DONE;
                    currentTokenType = EXP_EQ;
                }
                else {
                    // 非法
                    state = ERROR;
                    pos--; // 回到上一个
                    currentToken.pop_back(); // 去掉这个c
                }
                break;
            case IN_REG:
                if (c == '$') {
                    // 正则匹配结束
                    state = DONE;
                    if (tokens.back().type == RE_CONCAT) {
                        // 上一个是&的话，要把&弹掉
                        tokens.pop_back();
                    }
                    currentTokenType = RE_RIGHT_FLAG;
                }
                else if (regToken2tokenType.count(c)) {
                    // 正则运算符
                    state = REG_DONE;
                    currentTokenType = regToken2tokenType[c];
                }
                else {
                    // 正则字符
                    state = REG_DONE;
                    currentTokenType = RE_CHAR;
                }
                break;
            case REG_DONE:
                pos--; // 回到上一个
                currentToken.pop_back(); // 去掉这个c
            // 正则符号和变量单独讨论
                if (currentTokenType == RE_CHAR) {
                    tokens.emplace_back(currentTokenType, currentToken);
                    // 手动添加连接符号
                    tokens.emplace_back(RE_CONCAT, std::string(1, '&'));
                }
                else {
                    if (!tokens.empty() && tokens.back().type == RE_CONCAT) {
                        if (currentTokenType != RE_CONCAT && currentTokenType !=
                            RE_LEFT_MID && currentTokenType != RE_LEFT_SMALL && currentTokenType != RE_ANY) {
                            // & [ ( . 上一个是&，要把&弹掉
                            tokens.pop_back();
                        }
                    }
                    if (currentTokenType == RE_LINE_IN_MID) {
                        tokens.back().type = RE_CHAR_BEFORE_LINE;
                    }
                    tokens.emplace_back(currentTokenType, currentToken);
                    if (currentTokenType != RE_LINE_IN_MID && currentTokenType != RE_CONCAT && currentTokenType !=
                        RE_LEFT_MID && currentTokenType != RE_LEFT_SMALL && currentTokenType != RE_OR) {
                        // - & [ ( | 后面要加&
                        tokens.emplace_back(RE_CONCAT, std::string(1, '&'));
                    }
                }
                currentToken = ""; // 清空token
                state = IN_REG; // 回到REG的自动机而不是START
                break;
            case DONE:
                pos--; // 回到上一个
                currentToken.pop_back(); // 去掉这个c
                if (!currentToken.empty()) {
                    // 排除注释的情况
                    tokens.emplace_back(currentTokenType, currentToken);
                }
                currentToken = ""; // 清空token
                state = START;
                break;
            case ERROR:
//                std::cout << "[ERROR] currentToken: " << currentToken << "\tcurrentTokenType: " << currentTokenType <<
//                    std::endl;
                return tokens;
            default:
                break;
        }
    }
    return tokens;
}
